package org.opencv.android;

import java.io.File;
import java.util.StringTokenizer;

import org.opencv.core.Core;
import org.opencv.engine.OpenCVEngineInterface;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

class AsyncServiceHelper {
	public static boolean initOpenCV(String Version, final Context AppContext, final LoaderCallbackInterface Callback) {
		AsyncServiceHelper helper = new AsyncServiceHelper(Version, AppContext, Callback);
		Intent intent = new Intent("org.opencv.engine.BIND");
		intent.setPackage("org.opencv.engine");
		if (AppContext.bindService(intent, helper.mServiceConnection, Context.BIND_AUTO_CREATE)) {
			return true;
		} else {
			AppContext.unbindService(helper.mServiceConnection);
			InstallService(AppContext, Callback);
			return false;
		}
	}

	protected AsyncServiceHelper(String Version, Context AppContext, LoaderCallbackInterface Callback) {
		mOpenCVersion = Version;
		mUserAppCallback = Callback;
		mAppContext = AppContext;
	}

	protected static final String TAG = "OpenCVManager/Helper";
	protected static final int MINIMUM_ENGINE_VERSION = 2;
	protected OpenCVEngineInterface mEngineService;
	protected LoaderCallbackInterface mUserAppCallback;
	protected String mOpenCVersion;
	protected Context mAppContext;
	protected static boolean mServiceInstallationProgress = false;
	protected static boolean mLibraryInstallationProgress = false;

	protected static boolean InstallServiceQuiet(Context context) {
		boolean result = true;
		try {
			Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(OPEN_CV_SERVICE_URL));
			intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			context.startActivity(intent);
		} catch (Exception e) {
			result = false;
		}

		return result;
	}

	protected static void InstallService(final Context AppContext, final LoaderCallbackInterface Callback) {
		if (!mServiceInstallationProgress) {
			Log.d(TAG, "Request new service installation");
			InstallCallbackInterface InstallQuery = new InstallCallbackInterface() {
				private LoaderCallbackInterface mUserAppCallback = Callback;

				public String getPackageName() {
					return "OpenCV Manager";
				}

				public void install() {
					Log.d(TAG, "Trying to install OpenCV Manager via Google Play");

					boolean result = InstallServiceQuiet(AppContext);
					if (result) {
						mServiceInstallationProgress = true;
						Log.d(TAG, "Package installation started");
					} else {
						Log.d(TAG, "OpenCV package was not installed!");
						int Status = LoaderCallbackInterface.MARKET_ERROR;
						Log.d(TAG, "Init finished with status " + Status);
						Log.d(TAG, "Unbind from service");
						Log.d(TAG, "Calling using callback");
						mUserAppCallback.onManagerConnected(Status);
					}
				}

				public void cancel() {
					Log.d(TAG, "OpenCV library installation was canceled");
					int Status = LoaderCallbackInterface.INSTALL_CANCELED;
					Log.d(TAG, "Init finished with status " + Status);
					Log.d(TAG, "Calling using callback");
					mUserAppCallback.onManagerConnected(Status);
				}

				public void wait_install() {
					Log.e(TAG, "Instalation was not started! Nothing to wait!");
				}
			};

			Callback.onPackageInstall(InstallCallbackInterface.NEW_INSTALLATION, InstallQuery);
		} else {
			Log.d(TAG, "Waiting current installation process");
			InstallCallbackInterface WaitQuery = new InstallCallbackInterface() {
				private LoaderCallbackInterface mUserAppCallback = Callback;

				public String getPackageName() {
					return "OpenCV Manager";
				}

				public void install() {
					Log.e(TAG, "Nothing to install we just wait current installation");
				}

				public void cancel() {
					Log.d(TAG, "Wating for OpenCV canceled by user");
					mServiceInstallationProgress = false;
					int Status = LoaderCallbackInterface.INSTALL_CANCELED;
					Log.d(TAG, "Init finished with status " + Status);
					Log.d(TAG, "Calling using callback");
					mUserAppCallback.onManagerConnected(Status);
				}

				public void wait_install() {
					InstallServiceQuiet(AppContext);
				}
			};

			Callback.onPackageInstall(InstallCallbackInterface.INSTALLATION_PROGRESS, WaitQuery);
		}
	}

	/**
	 * URL of OpenCV Manager page on Google Play Market.
	 */
	protected static final String OPEN_CV_SERVICE_URL = "market://details?id=org.opencv.engine";

	protected ServiceConnection mServiceConnection = new ServiceConnection() {
		public void onServiceConnected(ComponentName className, IBinder service) {
			Log.d(TAG, "Service connection created");
			mEngineService = OpenCVEngineInterface.Stub.asInterface(service);
			if (null == mEngineService) {
				Log.d(TAG, "OpenCV Manager Service connection fails. May be service was not installed?");
				InstallService(mAppContext, mUserAppCallback);
			} else {
				mServiceInstallationProgress = false;
				try {
					if (mEngineService.getEngineVersion() < MINIMUM_ENGINE_VERSION) {
						Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INCOMPATIBLE_MANAGER_VERSION);
						Log.d(TAG, "Unbind from service");
						mAppContext.unbindService(mServiceConnection);
						Log.d(TAG, "Calling using callback");
						mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INCOMPATIBLE_MANAGER_VERSION);
						return;
					}

					Log.d(TAG, "Trying to get library path");
					String path = mEngineService.getLibPathByVersion(mOpenCVersion);
					if ((null == path) || (path.length() == 0)) {
						if (!mLibraryInstallationProgress) {
							InstallCallbackInterface InstallQuery = new InstallCallbackInterface() {
								public String getPackageName() {
									return "OpenCV library";
								}

								public void install() {
									Log.d(TAG, "Trying to install OpenCV lib via Google Play");
									try {
										if (mEngineService.installVersion(mOpenCVersion)) {
											mLibraryInstallationProgress = true;
											Log.d(TAG, "Package installation statred");
											Log.d(TAG, "Unbind from service");
											mAppContext.unbindService(mServiceConnection);
										} else {
											Log.d(TAG, "OpenCV package was not installed!");
											Log.d(TAG, "Init finished with status "
													+ LoaderCallbackInterface.MARKET_ERROR);
											Log.d(TAG, "Unbind from service");
											mAppContext.unbindService(mServiceConnection);
											Log.d(TAG, "Calling using callback");
											mUserAppCallback.onManagerConnected(LoaderCallbackInterface.MARKET_ERROR);
										}
									} catch (RemoteException e) {
										e.printStackTrace();
										;
										Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INIT_FAILED);
										Log.d(TAG, "Unbind from service");
										mAppContext.unbindService(mServiceConnection);
										Log.d(TAG, "Calling using callback");
										mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INIT_FAILED);
									}
								}

								public void cancel() {
									Log.d(TAG, "OpenCV library installation was canceled");
									Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INSTALL_CANCELED);
									Log.d(TAG, "Unbind from service");
									mAppContext.unbindService(mServiceConnection);
									Log.d(TAG, "Calling using callback");
									mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INSTALL_CANCELED);
								}

								public void wait_install() {
									Log.e(TAG, "Instalation was not started! Nothing to wait!");
								}
							};

							mUserAppCallback.onPackageInstall(InstallCallbackInterface.NEW_INSTALLATION, InstallQuery);
						} else {
							InstallCallbackInterface WaitQuery = new InstallCallbackInterface() {
								public String getPackageName() {
									return "OpenCV library";
								}

								public void install() {
									Log.e(TAG, "Nothing to install we just wait current installation");
								}

								public void cancel() {
									Log.d(TAG, "OpenCV library installation was canceled");
									mLibraryInstallationProgress = false;
									Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INSTALL_CANCELED);
									Log.d(TAG, "Unbind from service");
									mAppContext.unbindService(mServiceConnection);
									Log.d(TAG, "Calling using callback");
									mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INSTALL_CANCELED);
								}

								public void wait_install() {
									Log.d(TAG, "Waiting for current installation");
									try {
										if (!mEngineService.installVersion(mOpenCVersion)) {
											Log.d(TAG, "OpenCV package was not installed!");
											Log.d(TAG, "Init finished with status "
													+ LoaderCallbackInterface.MARKET_ERROR);
											Log.d(TAG, "Calling using callback");
											mUserAppCallback.onManagerConnected(LoaderCallbackInterface.MARKET_ERROR);
										} else {
											Log.d(TAG, "Wating for package installation");
										}

										Log.d(TAG, "Unbind from service");
										mAppContext.unbindService(mServiceConnection);

									} catch (RemoteException e) {
										e.printStackTrace();
										Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INIT_FAILED);
										Log.d(TAG, "Unbind from service");
										mAppContext.unbindService(mServiceConnection);
										Log.d(TAG, "Calling using callback");
										mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INIT_FAILED);
									}
								}
							};

							mUserAppCallback.onPackageInstall(InstallCallbackInterface.INSTALLATION_PROGRESS,
									WaitQuery);
						}
						return;
					} else {
						Log.d(TAG, "Trying to get library list");
						mLibraryInstallationProgress = false;
						String libs = mEngineService.getLibraryList(mOpenCVersion);
						Log.d(TAG, "Library list: \"" + libs + "\"");
						Log.d(TAG, "First attempt to load libs");
						int status;
						if (initOpenCVLibs(path, libs)) {
							Log.d(TAG, "First attempt to load libs is OK");
							String eol = System.getProperty("line.separator");
							for (String str : Core.getBuildInformation().split(eol))
								Log.i(TAG, str);

							status = LoaderCallbackInterface.SUCCESS;
						} else {
							Log.d(TAG, "First attempt to load libs fails");
							status = LoaderCallbackInterface.INIT_FAILED;
						}

						Log.d(TAG, "Init finished with status " + status);
						Log.d(TAG, "Unbind from service");
						mAppContext.unbindService(mServiceConnection);
						Log.d(TAG, "Calling using callback");
						mUserAppCallback.onManagerConnected(status);
					}
				} catch (RemoteException e) {
					e.printStackTrace();
					Log.d(TAG, "Init finished with status " + LoaderCallbackInterface.INIT_FAILED);
					Log.d(TAG, "Unbind from service");
					mAppContext.unbindService(mServiceConnection);
					Log.d(TAG, "Calling using callback");
					mUserAppCallback.onManagerConnected(LoaderCallbackInterface.INIT_FAILED);
				}
			}
		}

		public void onServiceDisconnected(ComponentName className) {
			mEngineService = null;
		}
	};

	private boolean loadLibrary(String AbsPath) {
		boolean result = true;

		Log.d(TAG, "Trying to load library " + AbsPath);
		try {
			System.load(AbsPath);
			Log.d(TAG, "OpenCV libs init was ok!");
		} catch (UnsatisfiedLinkError e) {
			Log.d(TAG, "Cannot load library \"" + AbsPath + "\"");
			e.printStackTrace();
			result &= false;
		}

		return result;
	}

	private boolean initOpenCVLibs(String Path, String Libs) {
		Log.d(TAG, "Trying to init OpenCV libs");
		if ((null != Path) && (Path.length() != 0)) {
			boolean result = true;
			if ((null != Libs) && (Libs.length() != 0)) {
				Log.d(TAG, "Trying to load libs by dependency list");
				StringTokenizer splitter = new StringTokenizer(Libs, ";");
				while (splitter.hasMoreTokens()) {
					String AbsLibraryPath = Path + File.separator + splitter.nextToken();
					result &= loadLibrary(AbsLibraryPath);
				}
			} else {
				// If the dependencies list is not defined or empty.
				String AbsLibraryPath = Path + File.separator + "libopencv_java3.so";
				result &= loadLibrary(AbsLibraryPath);
			}

			return result;
		} else {
			Log.d(TAG, "Library path \"" + Path + "\" is empty");
			return false;
		}
	}
}
